/*
 * Copyright (C) 2017-2018 Alibaba Group Holding Limited. All Rights Reserved.
 *
 *
 * Licensed under the Apache License, Version 2.0 (the License);
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#ifndef ALIOS_SPEECH_ASR_V2_H_
#define ALIOS_SPEECH_ASR_V2_H_

#include <stdint.h>
#include <string>
#include <memory>
#include "speech_const.h"

/**
 * @file speech_asr_v2.h
 * @brief This file contains ASRLocalEngineV2/ASRCloudEngineV2 public interface defined by AliOS,
 * and is shared by the platform independent voice framework and the vendor specific Implementation.
 **/

namespace alios {

class ASRLocalEngineListenerV2 {
public:
  ASRLocalEngineListenerV2() {}
  virtual ~ASRLocalEngineListenerV2() {}
  /**
   * @brief notify ASRLocalEngineListenerV2 recoginition result
   * when ASRLocalEngineV2 have recoginition result
   * @param[in] result string of recoginition
   * @param[result_state] state for result of recoginition
   * ASRSrNullResultForTimeout for null result with timeout
   * ASRSrPartialResult for partial result
   * ASRSrFinalResult for final result
   * result string streamed with json format, for example:
   * '{"results":[{"nbest":[{"confidence":37.737178802490234,//本地识别打分，仲裁需要，超过阈值
   * "raw_confidence":-4.7886042594909668, // 原生打分，仲裁需要
   * "text":"打开", //识别结果文字 "words": // 建议提供，其中包括发音
   * [{"confidence":54.609428405761719,"end_time":17,"pronunciation":"da3",
   * "raw_confidence":-4.0960578918457031,"start_time":12,"word":"打"},
   * {"confidence":82.260780334472656,"end_time":21,"pronunciation":"kai1",
   * "raw_confidence":-2.73321533203125,"start_time":18,"word":"开"}]}],"source":"jsgf"
   * //识别结果来源，需要指定}],"version":"aispeech1.0",//本地识别引擎版本
   * "state":"final" // 识别结果状态，最终结果或者中间结果temporary}'
   */
  virtual void OnRecognitionResult(AsrResultState result_state, const JSON &result) = 0;

  /**
   * @brief notify ASRLocalEngineListenerV2 recoginition result error
   * when ASRLocalEngineV2 have recoginition result error.
   * @param[result_state] state for result of recoginition
   * @param[error] for error reason code
   */
  virtual void OnRecognitionError(AsrErrorCode error, std::string &error_info) = 0;

  /**
   * @brief notify ASRLocalEngineListenerV2 ASRLocalEngineV2 cancel asr process
   * when ASRLocalEngineV2 is called by Cancel.
   */
  virtual void OnCancelled() = 0;
};

class ASRLocalEngineV2 {
public:
  ASRLocalEngineV2() {}
  virtual ~ASRLocalEngineV2() {}
  /**
   * @brief SetQueryId to ASRCloudEngineV2 for identify asr session queryid.
   * @param[query_id] uuid for ASRCloudEngineV2 identify asr process id
   * its content should be contained in asr result value with uid key.
  */
  virtual void SetQueryId(const std::string &query_id) = 0;
  /**
   * @brief set ASRLocalEngineListenerV2 listener to ASRLocalEngineV2 for notify info from ASRLocalEngineV2
   * @param[listener] for receive notify from ASRLocalEngineV2
   * listener can be null or one customed object of subclass ASRLocalEngineListenerV2.
   */
  virtual void SetListener(ASRLocalEngineListenerV2 *listener) = 0;

  // initialize the asr, vad, and opus engines.
  /**
   * @brief initialize ASRLocalEngineV2
   * @param[assets_path] assets path for ASRLocalEngineV2 will acces resources
   * @param[save_path] for ASRLocalEngineV2 can save tmp data path
   * @param[lang_type] for ASRLocalEngineV2 recognition lang
   * @return[retcode] return ASRLocalEngineV2 result for Initialize.
   */
  virtual RetCode Initialize(const std::string &assets_path,
                    const std::string &save_path = "",
                    LanguageType lang_type = Mandarin) = 0;

  /**
   * @brief uninitialize to ASRLocalEngineV2
   */
  virtual RetCode Uninitialize() = 0;

  /**
   * @brief set grammar content and its tag to ASRLocalEngineV2
   * @param[grammar_tag] grammar tag for grammars ASRLocalEngineV2 will use
   * @param[grammar_content] grammar content ASRLocalEngineV2 will use
   * its contents are jsgf format
   * @param[is_overwrite] ASRLocalEngineV2 will overwrite its previous grammars or not
   * @param[is_immediatly_active] ASRLocalEngineV2 active new grammar or not
   * @param[lang_type] set ASRLocalEngineV2 new grammar lang
   * @param[grammar_type] set ASRLocalEngineV2 new grammar type lang
   * @return[retcode] return ASRLocalEngineV2 result for CompileGrammar.
   */
  virtual RetCode CompileGrammar(const std::string &grammar_tag,
                        const JSON &grammar_content,
                        const JSON &vocab_content,
                        bool is_overwrite, bool is_immediatly_active = false,
                        LanguageType lang_type = Mandarin,
                        AsrGrammarType grammar_type = AsrJSGF) = 0;

  /**
   * @brief set ASRLocalEngineV2 start audio recognition
   * @param[grammar_tag] grammar tag for grammars ASRLocalEngineV2 will use
   * @param[is_need_result] set ASRLocalEngineV2 will notify asr result or not
   * @return[retcode] return ASRLocalEngineV2 result for Start.
   * if Start successfully, voice service will send audio data to ASRLocalEngineV2
   */
  virtual RetCode Start() = 0;

  /**
   * @brief set ASRLocalEngineV2 stop audio recognition
   * when ASRLocalEngineV2 stop, ASRLocalEngineV2 should notify ASRLocalEngineListenerV2.
   * @return[retcode] return ASRLocalEngineV2 result for Stop.
   */
  virtual RetCode Stop() = 0;

  /**
   * @brief set ASRLocalEngineV2 cancel audio recognition
   * when ASRLocalEngineV2 finish cancel, ASRLocalEngineV2 should notify ASRLocalEngineListenerV2 OnCancelled.
   * @return[retcode] return ASRLocalEngineV2 result for Cancel.
   */
  virtual RetCode Cancel() = 0;

  /**
   * @brief Send Audio Data to ASRLocalEngineV2
   * @param[wav_data] audio pcm data with audio format.
   * @param[sample_num] wav_data contains sample number.
   * @return[retcode] return ASRLocalEngineV2 result for UpdateAudioData.
   */
  virtual void UpdateVoiceData(const AudioEncoder encoder, const int8_t *data, const int length) = 0;

  /**
   * @brief rescore nlu result with ASRLocalEngineV2
   * @param[nlu_result_json] nlu_result_json should rescored with ASRLocalEngineV2
   * @param[rescored_slot_json] rescore result for nlu_result_json
   * @return[retcode] return ASRLocalEngineV2 result for RescoreResult.
   */
  virtual RetCode RescoreResult(const JSON &nlu_result_json, JSON &rescored_slot_json) { return alios::FuncNoSupport; }
};

class ASRCloudEngineListenerV2 {
public:
  ASRCloudEngineListenerV2() {}
  virtual ~ASRCloudEngineListenerV2() {}

public:
  /**
   * @brief notify ASRCloudEngineListenerV2 recoginition result
   * when ASRCloudEngineV2 have recoginition result
   * @param[in] result string of recoginition
   * @param[result_state] state for result of recoginition
   * ASRSrNullResultForTimeout for null result with timeout
   * ASRSrPartialResult for partial result
   * ASRSrFinalResult for final result
   * result string streamed with json format, for example:
   * '{"results":{"asr_out":{ // asr识别结果输出
   * "duration":1787, // 语音时间长度单位ms "result":"打开", // 识别结果
   * "uid":"f428057fefad851f4b7555762f76848d",
   * // 唯一id，框架会在调用ASRCloudEngine StartVoice之前，通过调用SetQueryId来设置
   * "confidence":1.0, // 识别打分,预留未来参与仲裁 "finish":1, // 已完成, 0 表示中间结果
   * "version":"4.0", // 云端asr识别版本
   * "pronunciation":"da3 kai2", // 识别结果发音
   * "status":1 // 识别状态，内部保留字段，可不支持}}}'
   */
  virtual void OnRecognitionResult(AsrResultState result_state, const JSON &result) = 0;

  /**
   * @brief notify ASRCloudEngineListenerV2 recoginition result error
   * when ASRCloudEngineV2 have recoginition result error.
   * @param[result_state] state for result of recoginition
   * @param[error] for error reason code
   */
  virtual void OnRecognitionError(AsrErrorCode error, std::string &error_info) = 0;
};

class ASRCloudEngineV2 {
public:
  ASRCloudEngineV2() {}
  virtual ~ASRCloudEngineV2() {}
public:
  /**
   * @brief SetQueryId to ASRCloudEngineV2 for identify asr session queryid.
   * @param[query_id] uuid for ASRCloudEngineV2 identify asr process id
   * its content should be contained in asr result value with uid key.
   */
  virtual void SetQueryId(std::string &query_id) = 0;

  /**
   * @brief SetVocabulary for ASRCloudEngineV2
   * @return
   * @param[vocabulary] vocabulary for ASRCloudEngineV2
   * its content is json format {"word":weight} ,
   * weight 1-5
   * for example:
   * {"关羽":5,"张飞":5,"赵云":5}
   * it must be enabled when next calls for startVoice
   */
  virtual void SetVocabulary(JSON &vocabulary) = 0;

  /**
   * @brief set asr listener for  ASRCloudEngineV2
   * @return
   * @param[listener] listener for ASRCloudEngineV2
   */
  virtual void SetListener(ASRCloudEngineListenerV2 *listener) = 0;

  // initialize the asr cloud engine.
  /**
   * @brief initialize ASRCloudEngineV2
   * @param[configs] for ASRCloudEngineV2 config infos
   * its content is json format
   * for example:
   * '{"configs":{"auth":{"uuid":"12345678","device":"AS22MC","version":"2.3.1"},
   *  "i18n":{"lang":"zh-CN"}}}
   * it maybe called before Start and once
   * if engine impl use auth config and chek fail, it should notify
   * in listener with error.
   * if framework change auth config, it'll call UpdateConfigs and
   * engine impl should use new configs and check again.
   * @return[retcode] return ASRCloudEngineV2 result for Initialize.
   */
  virtual RetCode Initialize(JSON configs) { return alios::FuncNoSupport; }

  /**
   * @brief uninitialize to ASRCloudEngineV2
   */
  virtual RetCode Uninitialize() { return alios::FuncNoSupport; }

  // update configure infos.
  /**
   * @brief update system config info for ASRCloudEngineV2
   * @param[configs] for ASRCloudEngineV2 sys config infos
   * its content is json format
   * for example:
   * '{"configs":{"auth":{"uuid":"12345678", "device":"AS22MC", "version":"2.3.1"},
   *  "network":{"net_type": 1, "conn_state": 0},
   *  "fullduplex":{"enable": 1, "min_confidence": 0.2}}}'
   * it can be called many times, and only with some fields to change.
   * if engine impl use auth config and chek again, if check fail, it should notify
   * in listen with error.
   * @return.
   */
  virtual void UpdateConfigs(JSON configs) {}

  /**
   * @brief called when start begin, to connect, build and send protocal header etc.
   * @return
   */
  virtual void Start() = 0;

  /**
   * @brief called when voice data reach
   * @return
   * @param[encoder] encoder
   * @param[data] data
   * @param[length] length
   */
  virtual void UpdateVoiceData(const AudioEncoder encoder, const int8_t *data, const int length) = 0;

  /**
   * @brief called when voice end,to tell the server stop.
   * @return
   */
  virtual void Stop() = 0;

  /**
   * @brief to cancle this session of asr .
   * @return
   */
  virtual void Cancel() = 0;
};

class WakeupEngineListener {
public:
  WakeupEngineListener() {}
  virtual ~WakeupEngineListener() {}
  /**
   * @brief notify WakeupEngineListener begin processing keyword
   * when WakeupEngine begin process keyword
   * @param[keyword_info] which keyword
   */
  virtual void OnKeywordStart(const JSON &keyword_info) = 0;

  /**
   * @brief notify WakeupEngineListener keyword audio opus data
   * when WakeupEngine process keyword
   * @param[encoder] audio encoder
   * @param[data] binary voice data encoded with ogg/opus format
   * @param[length] voice data length
   */
  virtual void OnKeywordData(const alios::AudioEncoder encoder, const unsigned char *data, int length) = 0;

  /**
   * @brief notify WakeupEngineListener end of receiving keyword voice data
   * when WakeupEngine finish keyword voice data.
   */
  virtual void OnKeywordEnd() = 0;

  /**
   * @brief notify WakeupEngineListener keyword spot
   * when WakeupEngine spot one keyword
   * @param[kws_result] spotted keyword result
   */
  virtual void OnKeywordSpotted(const KwsResult &kws_result) = 0;

  /**
   * @brief notify WakeupEngineListener vad front timeout
   * when WakeupEngine not receive user input useful voice data before vad front timeout.
   */
  virtual void OnVadFrontTimeout() = 0;

  /**
   * @brief notify WakeupEngineListener begin having voice data
   * when WakeupEngine receive user input useful voice data,
   * which except vad and noise data.
   * if oneshot_mode is 0, it's not one shot, else if oneshot_mode is 1, audio data is whole,
   * if oneshot_mode is 2, audio data is cut off
   */
  virtual void OnVoiceStart(OneShotMode mode) = 0;

    /**
   * @brief notify WakeupEngineListener opus data
   * when WakeupEngine receive user input useful voice data
   * @param[oneshot_type] if oneshot_mode is 0, it's not oneshot,
   * @param[data] binary voice data
   * @param[length] voice data length
   */
  virtual void OnVoiceData(const alios::AudioEncoder encoder, const int8_t *data, int length) = 0;

  /**
   * @brief notify WakeupEngineListener end of receiving voice data
   * when WakeupEngine finish voice data which except vad and noise data.
   */
  virtual void OnVoiceEnd(bool quiet = false) = 0;
};

class WakeupEngine {
public:
  WakeupEngine() {}
  virtual ~WakeupEngine() {}

  /**
   * @brief set ASRLocalEngineListenerV2 listener to ASRLocalEngineV2 for notify info from ASRLocalEngineV2
   * @param[listener] for receive notify from ASRLocalEngineV2
   * listener can be null or one customed object of subclass ASRLocalEngineListenerV2.
   */
  virtual void SetListener(WakeupEngineListener *listener) = 0;

  /**
   * @brief get ASRLocalEngineV2 keywords params
   * @return for keywords used in ASRLocalEngineV2
   * @param[isMinor] whether for minor kws module
   * its content is json format, for example:
   * '{"word_list":[{"name":"你好小","type":"wakeup","activation":true,"is_main":true}]}'
   */
  virtual const JSON GetKeywordsParams(bool isMinor = false) const = 0;

  /**
   * @brief set keywords params to ASRLocalEngineV2
   * @return result of set keywords
   * @param[keywords_params] keywords to ASRLocalEngineV2
   * @param[isMinor] whether for minor kws module
   * its content is json format, for example:
   * '{"keyword_list":[{"keyword":0, "enabled":true}]}'
   */
  virtual RetCode SetKeywordsParams(const JSON &keywords_params, bool isMinor = false) = 0;

  /**
   * @brief set wakeup keywords params to WakeupEngine
   * @return result of set keywords
   * @param[keywords_params] keywords to WakeupEngine
   * its content is json format, for example:
   * '{"word_list":[{"name":"你好小张"},{"name":"你好小明"}]}'
   * @return[retcode] return WakeupEngine result for set wakeup keywords.
   */
   virtual RetCode SetWakeupKeywordsParams(const JSON &keywords_params, KeywordMode keyword_mode) = 0;

  /**
   * @brief set custom keywords to ASRLocalEngineV2
   * @param[custom_keywords] custom keywords ASRLocalEngineV2 will use
   * custom_keywords string is json format, for example:
   * '{"word_list":[{"name":"上一个"},{"name":"下一个"},{"name":"取消收藏"}]}'
   * @return[retcode] return ASRLocalEngineV2 result for set custom keywords.
   */
  virtual RetCode SetCustomizedKeywords(const JSON &custom_keywords) = 0;

  /**
   * @brief set vad front time for timeout to ASRLocalEngine
   * @param[time] time(ms) timeout for vad front with milliseconds
   * @return[retcode] return ASRLocalEngine support status.
   */
  virtual RetCode SetVadFrontTimeoutInterval(int32_t time) = 0;

  /**
   * @brief set audio format to ASRLocalEngineV2 which audio format data
   * @param[audio_foramt] ASRLocalEngineV2 will receive this format audio data.
   * @return[retcode] return ASRLocalEngineV2 support status.
   */
  virtual RetCode SetAudioFormat(AudioFormat audio_format) = 0;

  /**
   * @brief set active angle to ASRLocalEngineV2
   * @param[active_angle] active angle for ASRLocalEngineV2 will use
   * @return[retcode] return ASRLocalEngineV2 support status.
   */
   virtual RetCode SetActiveAngle(int32_t active_angle) { return alios::FuncNoSupport; }

  /**
   * @brief set voice zone to ASRLocalEngineV2
   * @param[active_zone] voice zone for ASRLocalEngineV2 will use
   * @return[retcode] return ASRLocalEngineV2 support status.
   */
  virtual RetCode SetActiveZone(VoiceZone active_zone) { return alios::FuncNoSupport; }

  // initialize the asr, vad, and opus engines.
  /**
   * @brief initialize ASRLocalEngineV2
   * @param[assets_path] assets path for ASRLocalEngineV2 will acces resources
   * @param[save_path] for ASRLocalEngineV2 can save tmp data path
   * @param[cust_conf] the customized resource(cfg, asp, kws...) file/folder names for different models,
   * it's json string, e.g. "{"asp":"xxx.modelY", "cfg":"xxx.modelZ"}"
   * @param[lang_type] for ASRLocalEngineV2 recognition lang
   * @return[retcode] return ASRLocalEngineV2 result for Initialize.
   */
  virtual RetCode Initialize(const std::string &assets_path,
                             const std::string &save_path = "",
                             const std::string &cust_conf = "",
                             LanguageType lang_type = Mandarin) = 0;

  /**
   * @brief uninitialize to ASRLocalEngineV2
   */
  virtual RetCode Uninitialize() = 0;

  /**
   * @brief set ASRLocalEngineV2 start audio recognition
   * @param[grammar_tag] grammar tag for grammars ASRLocalEngineV2 will use
   * @param[is_need_result] set ASRLocalEngineV2 will notify asr result or not
   * @return[retcode] return ASRLocalEngineV2 result for Start.
   * if Start successfully, voice service will send audio data to ASRLocalEngineV2
   */
  virtual RetCode Start(StartMode start_mode) = 0;

  /**
   * @brief set ASRLocalEngineV2 stop audio recognition
   * when ASRLocalEngineV2 stop, ASRLocalEngineV2 should notify ASRLocalEngineListenerV2.
   * @return[retcode] return ASRLocalEngineV2 result for Stop.
   */
  virtual RetCode Stop() = 0;

  /**
   * @brief Send Audio Data to ASRLocalEngineV2
   * @param[wav_data] audio pcm data with audio format.
   * @param[sample_num] wav_data contains sample number.
   * @return[retcode] return ASRLocalEngineV2 result for UpdateAudioData.
   */
  virtual RetCode UpdateAudioData(const int16_t *wav_data, int32_t sample_num) = 0;
};
}  // namespace alios

#if __GNUC__ >= 4
#  define ASR_EXPORT_API __attribute__ ((visibility ("default")))
#  define ASR_IMPORT_API __attribute__ ((visibility ("default")))
#  define ASR_INTERNAL   __attribute__ ((visibility ("hidden")))
#else
/** Visibility attribute to show method definitions */
#  define ASR_EXPORT_API
/** Visibility attribute to show declarations */
#  define ASR_IMPORT_API
/** Visibility attribute to hide declarations */
#  define ASR_INTERNAL
#endif

#define WAKEUP_CREATEENGINE_SYM "WAKEUP_CreateEngine"
#define WAKEUP_DESTROYENGINE_SYM "WAKEUP_DestroyEngine"
#define ASR_CREATELOCALENGINE_SYM "ASR_CreateLocalEngine"
#define ASR_DESTROYLOCALENGINE_SYM "ASR_DestroyLocalEngine"
#define ASR_CREATECLOUDENGINE_SYM "ASR_CreateCloudEngine"
#define ASR_DESTROYCLOUDENGINE_SYM "ASR_DestroyCloudEngine"

/*----------------------------------------------------------------------*/
/*                        Function Prototypes                           */
/*----------------------------------------------------------------------*/
#ifdef __cplusplus
extern "C" {
#endif

//
// ASR Engine library default entry points.
// ASR_CreateLocalEngine/ASR_DestroyLocalEngine.
// ASR_CreateCloudEngine/ASR_DestroyCloudEngine.
//
typedef alios::WakeupEngine* (*Wakeup_CreateEngineFunc)(void);
typedef alios::RetCode (*Wakeup_DestroyEngineFunc)(alios::WakeupEngine* engine);
typedef alios::ASRLocalEngineV2* (*ASR_CreateLocalEngineV2Func)(void);
typedef alios::RetCode (*ASR_DestroyLocalEngineV2Func)(alios::ASRLocalEngineV2* engine);
typedef alios::ASRCloudEngineV2* (*ASR_CreateCloudEngineV2Func)(void);
typedef alios::RetCode (*ASR_DestroyCloudEngineV2Func)(alios::ASRCloudEngineV2* engine);

#ifdef __cplusplus
}  /* end extern "C" */
#endif

#endif  // ALIOS_SPEECH_ASR_H_
